package cbbx_sm.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InvalidClassException;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import cbbx_sm.decision_maker.search.DynamicProgrammingLookahead;
import cbbx_sm.decision_maker.search.DynamicProgrammingTableEntry;
import cbbx_sm.decision_maker.search.WorldStateTransition;
import cbbx_sm.probabilistic_model.Cluster;
import cbbx_sm.probabilistic_model.ProbabilisticModel;
import edu.uci.ics.jung.graph.util.Pair;


public class LookaheadPredictorSerializator {
	public static String lookaheadFolder = ExperimentManager.lookaheadPolicyFolder;

	public static DynamicProgrammingLookahead generateLookaheadTable(ProbabilisticModel singleCamModel, double zoomu, double upu,
			int numberOfTimeStampsLookahead, boolean discounting, double error, int numberOfStates, boolean regenerate) throws Exception{
		if (regenerate) {
			deletePrevious(singleCamModel, zoomu, upu, numberOfTimeStampsLookahead, error, numberOfStates);
		}
		return camXXLookAheadTable(singleCamModel, zoomu, upu, numberOfTimeStampsLookahead, discounting, error, numberOfStates);
	}
	
	private static void deletePrevious(ProbabilisticModel singleCamModel, double utilityZoom, double utilityUP, int numberOfTimeStampsLookAhead, double epsilon, int numberOfStates){
		String tableOutputName = getLookAheadFileName(singleCamModel, utilityZoom, utilityUP, epsilon, numberOfStates, numberOfTimeStampsLookAhead);
		new File(lookaheadFolder + tableOutputName).delete();
	}
	
    public static Pair<Long> getSize(ProbabilisticModel singleCamModel, double utilityZoom, double utilityUP, int numberOfTimeStampsLookAhead, double epsilon, int numberOfStates){
      String tableOutputName = getLookAheadFileName(singleCamModel, utilityZoom, utilityUP, epsilon, numberOfStates, numberOfTimeStampsLookAhead);
      long tableSize = new File(lookaheadFolder + tableOutputName).length();
      return new Pair<Long>(tableSize, 0L);
      
  }
	
	private static DynamicProgrammingLookahead camXXLookAheadTable(ProbabilisticModel singleCamModel, double utilityZoom, double utilityUP,
			int numberOfTimeStampsLookAhead, boolean discounting, double error, int numberOfStates) throws Exception{
		DynamicProgrammingLookahead result = null;
		String tableOutputName = getLookAheadFileName(singleCamModel, utilityZoom, utilityUP, error, numberOfStates, numberOfTimeStampsLookAhead);
		if (new File(lookaheadFolder + tableOutputName).exists()){
			try {
				result = readUpScoreArray(singleCamModel, utilityZoom, utilityUP, numberOfTimeStampsLookAhead, error, numberOfStates);
				return result;
				// java.io.InvalidClassException
			} catch (Exception e) {
				
			}
		}
		
		// Generate the model.
		WorldStateTransition worldStateTransition = new WorldStateTransition(singleCamModel);
		result =
			new DynamicProgrammingLookahead(
					worldStateTransition, numberOfTimeStampsLookAhead,
					discounting ,error, numberOfStates, utilityZoom, utilityUP);

		// Write it down to disk to optimize simulation.
		LookaheadPredictorSerializator.writeDown(result, tableOutputName);
		
		return result;
	}

	private static void writeDown(Object object, String filename){
		try { 
			// Serialize to a file 
			ObjectOutput out = new ObjectOutputStream(new FileOutputStream(lookaheadFolder + filename)); 
			out.writeObject(object); 
			out.close(); 
		} 
		catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private static DynamicProgrammingLookahead readUpScoreArray(ProbabilisticModel singleCamModel, double utilityZoom,
			double utilityUP, int numberOfTimeStampsLookAhead, double error, int numberOfStates) throws InvalidClassException{
		String fileName = getLookAheadFileName(singleCamModel, utilityZoom, utilityUP, error, numberOfStates, numberOfTimeStampsLookAhead);

		DynamicProgrammingLookahead model = null;
		try { 
			// Deserialize from a file 
			File file = new File(lookaheadFolder + fileName); 
			ObjectInputStream in = new ObjectInputStream(new FileInputStream(file)); 
			// Deserialize the object javax.swing.JButton 
			model = (DynamicProgrammingLookahead) in.readObject(); 
			in.close(); 
		}
		// If the class was changed we need to regenerate at a higher level.
		catch (InvalidClassException e) {
			throw e;
		} 
		catch (IOException e) {
			e.printStackTrace();
		} 
		catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
		return model;
	}
	
	private static String getClusterIdsConcatenated(
      ProbabilisticModel singleCamModel) {
	     String clusters = "";
	     List<String> clusterIds = new ArrayList<String>();
	     for (Cluster cluster : singleCamModel.getFusedClusters()){
	    	 clusterIds.add(cluster.getUniqueId());
	     }
	     Collections.sort(clusterIds);
	     for (String clusterId : clusterIds){
	       clusters +=clusterId+"_";
	     }
      return clusters;
  }

	private static String getLookAheadFileName(
			ProbabilisticModel singleCamModel, double utilityZoom,
			double utilityUP, double error, int numberOfStates, int numberOfTimeStampsLookAhead) {
        String clusters = getClusterIdsConcatenated(singleCamModel);
		String returnString = "LookAheadPolicy_"+clusters+"_zoomU_"+utilityZoom+"_UPu_"+utilityUP+"_numStates_"+numberOfStates+"_error_"+error+"_lahead_"+numberOfTimeStampsLookAhead+".ser";
		return returnString.replace(" ", "").replace(",", "").replace("(","").replace(")","");
	}
	
	private static DynamicProgrammingTableEntry[][] readUp(String filename){
		DynamicProgrammingTableEntry[][] model = null;
		try { 
			// Deserialize from a file 
			File file = new File(lookaheadFolder + filename); 
			ObjectInputStream in = new ObjectInputStream(new FileInputStream(file)); 
			// Deserialize the object javax.swing.JButton 
			model = (DynamicProgrammingTableEntry[][]) in.readObject(); 
			in.close(); 
		} 
		catch (ClassNotFoundException e) { 
			e.printStackTrace();
		} 
		catch (IOException e) { 
			e.printStackTrace();
		} 
		
		return model;
	}
}
